package com.ruoyi.common.utils.file;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.SslUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.storage.model.FileListing;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;


@Component
public class QiniuImageUploader {

    protected final static Logger logger = LoggerFactory.getLogger(QiniuImageUploader.class);

    public static final String LOAD_ERR = "【附件拉取失败】";
    public static final String LOAD_ERR_BY_AUTH = "【附件拉取失败, 需验证】";
    public static final String ATT_TOO_LARGE = "【附件过大】";

    public static String BUCKET;

    @Value("${upload.bucket}")
    public void setBucket(String bucket){ QiniuImageUploader.BUCKET = bucket; }

    public static String getBucket() { return BUCKET;}

    public static String BUCKET_PRIV = "yamc";
    public static String DOMAIN_PRIV = "http://sf2if3a7c.hb-bkt.clouddn.com";


    public static String DOMAIN;

    @Value("${upload.domainOfBucket}")
    public void setDomain(String domain){ QiniuImageUploader.DOMAIN = domain; }

    public static String getDomain() { return DOMAIN; }

    // bucketCommon
    public static String BUCKET_COMMON = "yamc-common";
    @Value("${upload.bucketCommon}")
    public void setBucketCommon(String bucketCommon){ QiniuImageUploader.BUCKET_COMMON = bucketCommon; }
    public static String getBucketCommon() { return BUCKET_COMMON; };

    // domain-common
    public static String DOMAIN_COMMON = "http://sf5mu53q8.hb-bkt.clouddn.com";
    @Value("${upload.domainOfBucketCommon}")
    public void  setDomainCommon(String domainCommon){ QiniuImageUploader.DOMAIN_COMMON = domainCommon; }
    public static String getDomainCommon() { return DOMAIN_COMMON; }


    public static String ACCESS_KEY = "Z7T0-mFYt014yPmLzwZ4szFAhoYRir3iQz4vv9ve";
    public static String SECRET_KEY = "oWJ1WKGtQq00KyS-9A3XzYGTFfBx62xcT6I6hae8";

    @Value("${upload.accessKey}")
    public void setAccessKey(String accessKey){
        QiniuImageUploader.ACCESS_KEY = accessKey;
    }

    @Value("${upload.secretKey}")
    public void setSecretKey(String secretKey){
        QiniuImageUploader.SECRET_KEY = secretKey;
    }

    private static Auth auth;
    private static StringMap putPolicy = new StringMap();

    static {
        putPolicy.put("returnBody",
                "{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\","
                        + "\"fsize\":$(fsize),\"mimeType\":\"${mimeType}\",\"height\":\"${imageInfo.height}\","
                        + "\"width\":\"$(imageInfo.width)\",\"ext\":\"${ext}\"}");
    }

    public static Auth getAuth() {
        return auth == null ? Auth.create(ACCESS_KEY, SECRET_KEY): auth;
    }

    public static String getUpTokenByBucket(String bucket) {
        return getAuth().uploadToken(bucket, null, 3600, putPolicy);
    }

    @Async
    public void uploadKyData(byte[] imageData, String rename){
        upload(imageData, rename);
    }

    public static MyRet upload(byte[] imageData, String rename){
        // 去除空格和+
        rename = rename.replaceAll("\\+", "").replaceAll(" ", "");
        return upload(imageData, rename, getBucketCommon());
    }


    public static MyRet upload(byte[] imageData, String rename, String bucket) {
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone1());
        UploadManager uploadManager = new UploadManager(cfg);
        MyRet ret = null;
        try {
            Response res = uploadManager.put(imageData, rename, getUpTokenByBucket(bucket));
            logger.info(res.bodyString());
            ret = res.jsonToObject(MyRet.class);
            // 设置默认宽高
            ret = setDefaultSize(ret);
            ret.key =  rename;
            ret.url = getUrl(ret.key, bucket);
            ret.error = 0;
        } catch (QiniuException e) {
            Response r = e.response;
            // 请求失败时简单状态信息
            try {
                logger.error("上传失败，返回数据{}",r.bodyString());
            } catch (QiniuException e1) {
                e1.printStackTrace();
            }
        }
        return ret;
    }

    /**
     * @Author WD
     * @Description 删除文件（common_bucket）
     * @param key:
     * @return
     * @Date 2023/2/6 09:59
     */
    public static void delete(String key) {
        delete(key, getBucketCommon());
    }

    public static void delete(String key, String type) {
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone1());
        BucketManager bucketManager = new BucketManager(getAuth(), cfg);
        try {
            String bucket = null;
            if(StringUtils.equals(type, getBucketCommon())) {
                bucket = BUCKET_COMMON;
            } else {
                bucket = BUCKET;
            }
            Response res = bucketManager.delete(bucket, key);
            logger.info(res.bodyString());
        } catch (QiniuException e) {
            Response r = e.response;
            // 请求失败时简单状态信息
            try {
                logger.error("删除失败，返回数据{}",r.bodyString());
            } catch (QiniuException e1) {
                e1.printStackTrace();
            }
        }
    }

    public static boolean exists(String key) {
        Configuration cfg = new Configuration(Zone.zone1());
        BucketManager bucketManager = new BucketManager(getAuth(), cfg);
        try {
            FileInfo res = bucketManager.stat(BUCKET_COMMON, key);
            logger.info(res.toString());
            return StringUtils.isNotEmpty(res.hash);
        } catch (QiniuException e) {
            Response r = e.response;
            try {
                logger.error(r.bodyString());
            } catch (QiniuException e1) {
                e1.printStackTrace();
            }
        }
        return false;
    }

    /**
     * @Author WD
     * @Description 获取文件列表
     * @param prefix: key的前缀
     * @param marker: 分页标记，首次传null，下一页传上一页返回的marker
     * @param limit: 每页条数
     * @return
     * @Date 2023/5/24 16:13
     */
    public static FileListing list(String prefix, String marker, int limit) {
        //构造一个带指定Zone对象的配置类
        Configuration cfg = new Configuration(Zone.zone1());
        BucketManager manager = new BucketManager(getAuth(), cfg);
        try {
            return manager.listFiles(BUCKET_COMMON, prefix, marker, limit, null);
        } catch (QiniuException e) {
            Response r = e.response;
            // 请求失败时简单状态信息
            try {
                logger.error("请求失败，返回数据{}",r.bodyString());
            } catch (QiniuException e1) {
                e1.printStackTrace();
            }
        }
        return null;
    }

    public static String getUrl(String key) {
        return getUrl(key, getBucketCommon());
    }

    public static String getUrl(String key, String bucket) {
        String encodedFileName = null;
        try {
            encodedFileName = URLEncoder.encode(key, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String publicUrl =  String.format("%s/%s", getDomainCommon(), encodedFileName);
        // 公开资源，直接返回
        if(StringUtils.contains(bucket, "common")) {
            return publicUrl;
        }
        // 私有资源，设置链接过期时间
        publicUrl =  String.format("%s/%s", getDomain(), encodedFileName);
        return getAuth().privateDownloadUrl(publicUrl);
    }

    public static String getUrlByPriv(String key, String bucket){
        String encodedFileName = null;
        try {
            encodedFileName = URLEncoder.encode(key, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if(StringUtils.equals(bucket, BUCKET_PRIV)) {
            return getAuth().privateDownloadUrl(String.format("%s/%s", DOMAIN_PRIV, encodedFileName), 60*60*6);
        }
        return getUrl(key);
    }

    private static MyRet setDefaultSize(MyRet ret) {
        if(StringUtils.equals(ret.width, "null")) {
            ret.width = "-1";
        }
        if(StringUtils.equals(ret.height, "null")) {
            ret.height = "-1";
        }
        return ret;
    }

    /**
     * 获取 文件 流
     * @param url
     * @return
    */
    public static Map<String, Object> getFile(String url, int retryTimes) throws Exception {
        URL urlConet = new URL(url);
        if("https".equalsIgnoreCase(urlConet.getProtocol())){
            SslUtils.ignoreSsl();
        }
        String host = urlConet.getHost();
        HttpURLConnection con = (HttpURLConnection)urlConet.openConnection();
        con.setRequestMethod("GET");
        con.setConnectTimeout(10 * 1000);
        con.setReadTimeout(10 * 1000);
        con.setRequestProperty("Referer", host);
        con.setRequestProperty("Host", host);
        con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36");
        InputStream inStream = con.getInputStream();    //通过输入流获取图片数据
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len = 0; long byteSize = 0;
        while((len=inStream.read(buffer)) != -1 ){
            outStream.write(buffer, 0, len);
            // 判断大小，如果大于50M，就不上传了
            byteSize += len;
            if(byteSize >= 50 * 1024 * 1024) {
                return new HashMap(){{put("err", ATT_TOO_LARGE); }};
            }
        }
        inStream.close();
        if(StringUtils.contains(outStream.toString(), "html>")) {
            return new HashMap(){{put("err", LOAD_ERR_BY_AUTH); }};
        }
        // 服务器302，尝试重新下载
        if(con.getResponseCode() == 302 && StringUtils.isNotEmpty(con.getHeaderField("Location"))) {
            if(retryTimes > 2) {
                return new HashMap(){{put("err", LOAD_ERR); }};
            }
            return getFile(con.getHeaderField("Location"), retryTimes++);
        }
        if(byteSize == 0) {
            return new HashMap(){{put("err", LOAD_ERR); }};
        }
        byte[] data =  outStream.toByteArray();
        String fileName = "";
        String disposition = con.getHeaderField("Content-disposition");
        // 尝试获取文件名称
        if(StringUtils.isNotEmpty(disposition)) {
            String[] dis = disposition.split(";");
            for (String di : dis) {
                if (StringUtils.isNotEmpty(di) && StringUtils.contains(di, "filename")) {
                    fileName = di.split("=")[1];
                    if(StringUtils.contains(di.split("=")[0], "filename*")){
                        // 新标准,使用的是filename*
                        fileName = java.net.URLDecoder.decode(fileName, "UTF-8");
                        fileName = fileName.replace("utf-8''", "").trim();
                        break;
                    } else {
                        // 旧标准，使用的是filename
                        fileName = java.net.URLDecoder.decode(fileName, "UTF-8").trim();
                    }
                }
            }
        }
        if(StringUtils.isEmpty(fileName) && isAttachByUrl(url)){
            String[] ss = url.split("/");
            fileName = ss[ss.length - 1];
        }
        HashMap<String, Object> result = new HashMap<>();
        result.put("data", data);
        result.put("fileName", fileName);
        return result;
    }

    public static Map<String, String> getNewUrl(String url, String defaultFilePrefix){
        Map<String, String> result = new HashMap<>();
        try {
            Map<String, Object> fileMap = getFile(url, 1);
            if(fileMap == null || fileMap.size() == 0 || fileMap.containsKey("err")) {
                result.put("err", fileMap.getOrDefault("err", LOAD_ERR).toString());
                return result;
            }
            MyRet myRet = upload((byte[]) fileMap.get("data"), defaultFilePrefix + fileMap.getOrDefault("fileName", System.currentTimeMillis()), "bukaa-common");
            result.put("url", myRet.url);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        result.put("err", LOAD_ERR);
        return result;
    }

    public static MyRet saveAndGetNewUrl(String url, String defaultFilePrefix){
        try {
            Map<String, Object> fileMap = getFile(url, 1);
            if(fileMap == null || fileMap.size() == 0 || fileMap.containsKey("err")) {
                return null;
            }
            String fileName = fileMap.getOrDefault("fileName", System.currentTimeMillis()).toString();
            return upload((byte[]) fileMap.get("data"), defaultFilePrefix + fileName, "bukaa-common");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args){
        String url = "http://biocenter.swu.edu.cn/viscms/u/cms/biocenter/202004/2610434951jf/%E8%A5%BF%E5%8D%97%E5%A4%A7%E5%AD%A62020%E5%B9%B4%E7%A1%95%E5%A3%AB%E7%A0%94%E7%A9%B6%E7%94%9F%E5%A4%8D%E8%AF%95%E6%80%9D%E6%83%B3%E6%94%BF%E6%B2%BB%E8%80%83%E6%A0%B8%E8%87%AA%E8%AF%84%E8%A1%A8%E6%A8%A1%E6%9D%BF.docx";
        System.out.println(getNewUrl(url, "xbky/kyapp/schInfo/"));

        String rename = "http://www.baidu.com/ /+123";
        System.out.println(rename.replaceAll("\\+", "").replaceAll(" ", ""));
    }

    public static boolean isAttachByUrl(String url){
        String[] attEnd = new String[]{"pdf", "xls", "xlsx", "doc", "docx", "ppt", "pptx"};
        for (String ae: attEnd) {
            if(StringUtils.endsWith(url.trim().toLowerCase(), ae)) {
                return true;
            }
        }
        return false;
    }

    public class MyRet {
        public String width;
        public String height;
        public String url;
        public String key;
        public Integer error;
        public String mimeType;
        public String previewImage;
    }
}
